﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace LinqToDB.Provider
{
    /// <summary>
    /// 表达式简化
    /// </summary>
    class ExpressionSimplify:ExpressionVisitor
    {
        /// <summary>
        /// 构造简化器
        /// </summary>
        public ExpressionSimplify()
        {
        }

        /// <summary>
        /// 获取常量表达式中的值
        /// </summary>
        /// <param name="expresstion">表达式</param>
        /// <returns>常值</returns>
        object GetConstant(Expression expresstion)
        {
            if (expresstion == null)
                return null;
            return ((ConstantExpression)expresstion).Value;
        }
        /// <summary>
        /// 判断表达式是否为常量
        /// </summary>
        /// <param name="expresstion">表达式</param>
        /// <returns>是否为常量</returns>
        bool IsConstant(Expression expresstion)
        {
            if (expresstion==null)
                return true;

            if (expresstion is ConstantExpression)
                return true;
            else
                return false;
        }

        /// <summary>
        /// 判断表达式集合是否全为常量
        /// </summary>
        /// <param name="expresstions">表达式集合</param>
        /// <returns>是否为常量</returns>
        bool IsConstant(IEnumerable<Expression> expresstions)
        {
            foreach (Expression exp in expresstions)
            {
                if (!(exp is ConstantExpression))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 替换Lambda表达式中的参数，使得两个参数类型相同的表达式可以合并使用
        /// </summary>
        /// <param name="lambda">表达式</param>
        /// <param name="pList">替换的参数列表</param>
        /// <returns>替换后的表达式</returns>
        LambdaExpression ReplaceParam(LambdaExpression lambda, params ParameterExpression[] pList)
        {
            if (lambda.Parameters.Count != pList.Length)
                throw new NotSupportedException("不支持所指定的替换参数");
            Dictionary<ParameterExpression,Expression> dic = new Dictionary<ParameterExpression,Expression>();
            for(int i=0;i<lambda.Parameters.Count;i++)
            {
                dic.Add(lambda.Parameters[i],pList[i]);
            }

            ExpresstionReplaceParam replacer = new ExpresstionReplaceParam(dic);
            return (LambdaExpression)replacer.Visit(lambda);
        }

        /// <summary>
        /// 将两个Where语句合并为1个
        /// </summary>
        /// <param name="child">下一级Where语句</param>
        /// <param name="parent">上一级Where语句</param>
        /// <returns>合并后的表达式</returns>
        private LambdaExpression CombineWhere(LambdaExpression child, LambdaExpression parent)
        {
            child = ReplaceParam(child, parent.Parameters.ToArray());
            Expression expAnd = Expression.AndAlso(parent.Body, child.Body);
            var list = base.VisitExpressionList(parent.Parameters.Cast<Expression>().ToList().AsReadOnly());
            LambdaExpression lambd = Expression.Lambda(expAnd, list.Select(t => (ParameterExpression)t));
            return lambd;
        }

        /// <summary>
        /// 将两个Select语句合并为1个
        /// </summary>
        /// <param name="child">下一级Select语句</param>
        /// <param name="parent">上一级Select语句</param>
        /// <returns>合并后的表达式</returns>
        LambdaExpression CombineSelect(LambdaExpression child, LambdaExpression parent)
        {
            Dictionary<ParameterExpression,Expression> dic = new Dictionary<ParameterExpression,Expression>();
            dic.Add(child.Parameters[0],parent.Body);
            ExpresstionReplaceParam replacer = new ExpresstionReplaceParam(dic);
            Expression body = replacer.Visit(child.Body);
            return Expression.Lambda(body, parent.Parameters);
        }

        /// <summary>
        /// 转换常量表达式
        /// </summary>
        /// <param name="c">常量表达式</param>
        /// <returns>转换后的表达式</returns>
        internal override Expression VisitConstant(ConstantExpression c)
        {
            //简化常量套常量
            if (c.Value is ConstantExpression)
                return (ConstantExpression)c.Value;
            return base.VisitConstant(c);
        }
        /// <summary>
        /// 转换成员表达式
        /// </summary>
        /// <param name="c">成员表达式</param>
        /// <returns>转换后的表达式</returns>
        internal override Expression VisitMemberAccess(MemberExpression m)
        {
            Expression exp = this.Visit(m.Expression);
            if (IsConstant(exp))
            {
                if(m.Member is PropertyInfo)
                {
                    //简化常量属性
                    return Expression.Constant(((PropertyInfo)m.Member).GetValue(GetConstant(exp),null));
                }
                else if(m.Member is FieldInfo)
                {
                    //简化常量字段
                    return Expression.Constant(((FieldInfo)m.Member).GetValue(GetConstant(exp)));
                }
            }
            if (exp != m.Expression)
            {
                return Expression.MakeMemberAccess(exp, m.Member);
            }
            return Expression.MakeMemberAccess(exp,m.Member);
        }
        /// <summary>
        /// 转换方法调用表达式
        /// </summary>
        /// <param name="m">方法调用表达式</param>
        /// <returns>转换后的表达式</returns>
        internal override Expression VisitMethodCall(MethodCallExpression m)
        {
            Expression obj = this.Visit(m.Object);
            var args = this.VisitExpressionList(m.Arguments);
            
            //简化常量方法的调用
            if (IsConstant(obj) && IsConstant(args))
            {
                object objValue = m.Method.Invoke(GetConstant(obj), args.Select(t=>GetConstant(t)).ToArray());
                return Expression.Constant(objValue);
            }

            
            if (m.Method.DeclaringType == typeof(Queryable))
            {
                if (m.Method.Name == "Where")
                {
                    //连续两个Where语句可以合并
                    MethodCallExpression source1 = args[0] as MethodCallExpression;
                    LambdaExpression lambd1 = args[1] as LambdaExpression;
                    if (source1 != null && source1.Method.DeclaringType == typeof(Queryable) && source1.Method.Name == "Where")
                    {
                        Expression source2 = source1.Arguments[0];
                        LambdaExpression lambd2 = base.Visit(source1.Arguments[1]) as LambdaExpression;

                        LambdaExpression lambd = CombineWhere(lambd1, lambd2);
                        return Expression.Call(null, m.Method, source2, lambd);
                    }
                    
                }
                else if (m.Method.Name == "Select")
                {
                    //连续两个Select语句可以合并
                    MethodCallExpression source1 = args[0] as MethodCallExpression;
                    LambdaExpression lambd1 = args[1] as LambdaExpression;
                    if (source1 != null && source1.Method.DeclaringType == typeof(Queryable) && source1.Method.Name == "Select")
                    {
                        Expression source2 = source1.Arguments[0];
                        LambdaExpression lambd2 = base.Visit(source1.Arguments[1]) as LambdaExpression;

                        LambdaExpression lambd = CombineSelect(lambd1, lambd2);
                        Type sourceElement = TypeSystem.GetElementType( source2.Type);
                        Type resultElement = lambd1.ReturnType;
                        Type type = typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(sourceElement,resultElement));
                        MethodInfo method = TypeSystem.FindQueryableMethod("Select", 
                            new Type[] { source2.Type,type },sourceElement,resultElement);
                        return Expression.Call(null, method, source2, lambd);
                    }
                }
            }



            if (obj != m.Object || args != m.Arguments)
            {
                return Expression.Call(obj, m.Method, args);
            }
            return m;
        }




        /// <summary>
        /// 转换一元表达式
        /// </summary>
        /// <param name="u">一元表达式</param>
        /// <returns>转换后的表达式</returns>
        internal override Expression VisitUnary(UnaryExpression u)
        {
            Expression exp = base.Visit(u.Operand);
            switch (u.NodeType)
            {
                #region 去引用
                case ExpressionType.Quote:
                    return exp;
                #endregion

                #region 常量值计算
                case ExpressionType.Negate:
                case ExpressionType.Convert:
                case ExpressionType.Not:
                    if (IsConstant(exp))
                    {
                        LambdaExpression lab = Expression.Lambda(exp);
                        object value = lab.Compile().DynamicInvoke();
                        return Expression.Constant(value);
                    }
                    break;
                #endregion
            }
            return base.VisitUnary(u);
        }
        /// <summary>
        /// 转换二元达式
        /// </summary>
        /// <param name="b">二元表达式</param>
        /// <returns>转换后的表达式</returns>
        internal override Expression VisitBinary(BinaryExpression b)
        {
            Expression left = base.Visit(b.Left);
            Expression right = base.Visit(b.Right);
            switch(b.NodeType)
            {
                #region 简化逻辑运算
                case ExpressionType.AndAlso://简化逻辑与运算
                    if (IsConstant(left))
                    {
                        if (GetConstant(left).Equals(true))
                            return right;
                        else
                            return left;
                    }
                    if (IsConstant(right))
                    {
                        if (GetConstant(right).Equals(true))
                            return left;
                        else
                            return right;
                    }
                    break;
                case ExpressionType.OrElse://简化逻辑或运算
                    if (IsConstant(left))
                    {
                        if (GetConstant(left).Equals(true))
                            return left;
                        else
                            return right;
                    }
                    if (IsConstant(right))
                    {
                        if (GetConstant(right).Equals(true))
                            return right;
                        else
                            return left;
                    }
                    break; 
                #endregion

                #region 常量值计算
                case ExpressionType.Add:        //+
                case ExpressionType.Subtract:   //-
                case ExpressionType.Multiply:   //×
                case ExpressionType.Divide:     //÷
                case ExpressionType.Modulo:     //%
                case ExpressionType.ArrayIndex://简化常量数组的调用
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                    if (IsConstant(left) && IsConstant(right))
                    {
                        LambdaExpression lab = Expression.Lambda(b);
                        object value = lab.Compile().DynamicInvoke();
                        return Expression.Constant(value);
                    }
                    break;
                #endregion
            }
            return Expression.MakeBinary(b.NodeType,left,right);
        }

    }
}
